// 类
export class Greeter {
  greeting: string;
  constructor(message: string) {
    this.greeting = message;
  }
  greet() {
    return 'Hello,' + this.greeting;
  }
}
// 继承
class Animal {
  move(distanceInMeters: number = 0) {
    return `Animal moved ${distanceInMeters}m.`;
  }
}

export class Dog extends Animal {
  bark() {
    return 'Woof! Woof!';
  }
}
// 如何在子类里可以重写父类的方法
export class Animal1 {
  name: string;
  constructor(theName: string) {
    this.name = theName;
  }
  move(distanceInMeters: number = 0) {
    console.log(`${this.name} moved ${distanceInMeters}m.`);
  }
}

export class Snake extends Animal1 {
  constructor(name: string) {
    super(name);
  }
  move(distanceInMeters = 5) {
    console.log('Slithering...');
    super.move(distanceInMeters);
  }
}

export class Horse extends Animal1 {
  constructor(name: string) {
    super(name);
  }
  move(distanceInMeters = 45) {
    console.log('Galloping...');
    super.move(distanceInMeters);
  }
}
let sam = new Snake('Sammy the Python');
let tom: Animal1 = new Horse('Tommy the Palomino');

sam.move();
tom.move(34);
// 公共，私有与受保护的修饰符
// 1.默认为public
class Animal2 {
  public name: string;
  public constructor(theName: string) {
    this.name = theName;
  }
  public move(distanceInMeters: number) {
    console.log(`${this.name} moved ${distanceInMeters}m.`);
  }
}
// 2.私有private
class Animal3 {
  private name: string;
  constructor(theName: string) {
    this.name = theName;
  }
}
// new Animal3('Cat').name; // 错误: 'name' 是私有的.只能在类内部访问
class Animal4 {
  private name: string;
  constructor(theName: string) {
    this.name = theName;
  }
}
class Rhino extends Animal4 {
  constructor() {
    super('Rhino');
  }
}
class Employee {
  private name: string;
  constructor(theName: string) {
    this.name = theName;
  }
}
let animal = new Animal4('Goat');
let rhino = new Rhino();
let emoloyee = new Employee('Bob');
animal = rhino; // 因为Rhino是Animal的子类
// animal = emoloyee; // 错误: Animal 与 Employee 不兼容.
// 3.受保护的protected
class Person {
  protected name: string;
  constructor(name: string) {
    this.name = name;
  }
}
class Employee1 extends Person {
  private department: string;
  constructor(name: string, department: string) {
    super(name);
    this.department = department;
  }
  public getElevatorPitch() {
    return `Hello, my name is ${this.name} and I work in ${this.department}.`;
  }
}
let howard = new Employee1('Howard', 'Sales');
console.log(howard.getElevatorPitch());
// console.log(howard.name); // 错误,因为name是受保护的
// 构造函数也可以被标记成 protected
class Person2 {
  protected name: string;
  protected constructor(theName: string) {
    this.name = theName;
  }
}

// Employee 能够继承 Person
class Employee2 extends Person2 {
  private department: string;

  constructor(name: string, department: string) {
    super(name);
    this.department = department;
  }

  public getElevatorPitch() {
    return `Hello, my name is ${this.name} and I work in ${this.department}!`;
  }
}

let howard2 = new Employee2('Howard', 'Sales');
console.log(howard2.getElevatorPitch());
// let john = new Person2('John'); // 错误: 'Person' 的构造函数是被保护的.
// 4.readonly修饰符
class Octipus {
  readonly name: string;
  readonly numberOfLegs: number = 8;
  constructor(theName: string) {
    this.name = theName;
  }
}
let dad = new Octipus('Man with the 8 strong legs');
console.log(dad.name);
// dad.name = 'Man with the 3-piece suit'; // 错误! name 是只读的.
// 参数属性
// 参数属性通过给构造函数参数前面添加一个访问限定符来声明。 使用 private限定一个参数属性会声明并初始化一个私有成员；对于 public和 protected来说也是一样。注意看我们是如何舍弃了 theName，仅在构造函数里使用 readonly name: string参数来创建和初始化 name成员。 我们把声明和赋值合并至一处。调用时直接对象点属性或者方法即可
class Octipus1 {
  readonly numberOflegs: number = 8;
  constructor(readonly name: string) {}
}
let dad1 = new Octipus1('参数属性的使用');
console.log(dad1.name);
// 4.存取器 : 支持通过getters/setters来截取对对象成员的访问。 它能帮助你有效的控制对对象成员的访问。
let passcode = 'secret passcode';
class Employee3 {
  private _fullName: string;
  get fullName(): string {
    console.log(1);
    return this._fullName;
  }
  set fullName(name: string) {
    console.log(2);
    if (passcode && passcode == 'secret passcode') {
      this._fullName = name;
    } else {
      console.log('Error: Unauthorized update of employee!');
    }
  }
}
let employee = new Employee3();
employee.fullName = '';
if (employee.fullName) {
  console.log(employee.fullName);
}
//静态属性
class Grid {
  static origin = { x: 0, y: 0 };
  calculateDistanceFromOrigin(point: { x: number; y: number }) {
    let xDist = point.x - Grid.origin.x;
    let yDist = point.y - Grid.origin.y;
    return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale;
  }
  constructor(public scale: number) {}
}
let grid1 = new Grid(1.0);
let grid2 = new Grid(5.0);
console.log(grid1.calculateDistanceFromOrigin({ x: 10, y: 10 }));
console.log(grid2.calculateDistanceFromOrigin({ x: 10, y: 10 }));
// 抽象类  abstract关键字是用于定义抽象类和在抽象类内部定义抽象方法
abstract class Animal5 {
  abstract makeSound(): void;
  move(): void {
    console.log('roaming the earch...');
  }
}
// 抽象类中的抽象方法不包含具体实现并且必须在派生类中实现。 抽象方法的语法与接口方法相似。 两者都是定义方法签名但不包含方法体。 然而，抽象方法必须包含 abstract关键字并且可以包含访问修饰符。
abstract class Department {
  constructor(public name: string) {}
  printName(): void {
    console.log('Department name:' + this.name);
  }
  abstract printMeeting(): void;
}
class AccountingDepartment extends Department {
  constructor() {
    super('Accounting and Auditing'); // 在派生类的构造函数中必须调用 super()
  }
  printMeeting(): void {
    console.log('The Accounting Department meets each Monday at 10am.');
  }
  generateReports(): void {
    console.log('Generating accounting reports...');
  }
}
let department: Department; // 允许创建一个对抽象类型的引用
// department = new Department(); // 错误: 不能创建一个抽象类的实例
department = new AccountingDepartment(); // 允许对一个抽象子类进行实例化和赋值
department.printName();
department.printMeeting();
// department.generateReports(); // 错误: 方法在声明的抽象类中不存在
